/*
* Copyright 2011 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/
package org.etherframework.core
{
	import flash.display.DisplayObject;
	import flash.errors.IllegalOperationError;
	import flash.system.ApplicationDomain;
	
	import org.etherframework.IEther;
	import org.etherframework.common.DestructionPolicy;
	import org.etherframework.common.IDeferredDestroyable;
	import org.etherframework.common.IWeaklyDestroyable;
	import org.etherframework.common.command.ICommands;
	import org.etherframework.common.wire.IWiring;
	import org.etherframework.core.module.IModulePart;
	import org.etherframework.core.module.ModuleConfig;
	import org.etherframework.dependency.IDependencies;
	import org.etherframework.mediator.IMediators;
	
	public class Module implements IModule, IWeaklyDestroyable, IDeferredDestroyable
	{
		private var _access:String;
		private var _domain:String;
		private var _view:DisplayObject;
		private var _ether:IEther;
		
		private var _dependencies:IDependencies;
		private var _wiring:IWiring;
		private var _mediators:IMediators;
		private var _commands:ICommands;
		
		private var _attached:Boolean = false;
		private var _destroyed:Boolean = false;
		private var _destructionDelay:uint = 0;
		private var _destructionCondition:Boolean = true;
		private var _destructionPolicy:String = DestructionPolicy.NEVER;
		private var _initialized:Boolean = false;
		private var _started:Boolean = false;
		private var _config:ModuleConfig;
		
		public function Module(view:DisplayObject) 
		{
			_view = view;
		}
		
		public function initialize(config:ModuleConfig):void
		{
			if (_initialized)
				throw new Error("IModule is already initialized.");
			
			if (_destroyed)
				throw new Error("IModule is destroyed.");
			
			_config = config;
			_ether = _config.ether;
			
			init();
			
			_initialized = true;
		}
		
		public function start():void
		{
		}
		
		public function attach():void
		{
		}
		
		public function detach():void
		{
		}
		
		public function destroy():void
		{
			if (_dependencies)
				IModulePart(_dependencies).destroy();
			if (_wiring)
				IModulePart(_wiring).destroy();
			if (_mediators)
				IModulePart(_mediators).destroy();
			if (_commands)
				IModulePart(_commands).destroy();
			
			_view = null;
			_ether = null;
			_dependencies = null;
			_wiring = null;
			_mediators = null;
			_commands = null;
		}
		
		private function init():void
		{
			_dependencies = _config.dependencies;
			_mediators = _config.mediators;
			_wiring = _config.wiring;
			_commands = _config.commands;
			
			if (!_access)
				_access = _config.access;
			
			if (!_domain)
				_domain = _config.domain;
			
			if (_config.access != _access)
				_config.access = _access;
			
			if (_config.domain != _domain)
				_config.domain = _domain;
		}
		
		protected function completeParts():void
		{
			if (_dependencies)
				IModulePart(_dependencies).complete();
			
			if (_wiring)
				IModulePart(_wiring).complete();
			
			if (_mediators)
				IModulePart(_mediators).complete();
			
			if (_commands)
				IModulePart(_commands).complete();
		}
		
		public function persisted():void
		{
			
		}
		
		public function get domain():String { return _domain; }
		public function set domain(value:String):void 
		{
			if (_domain == value)
				return;
			
			_domain = value;
			
			if (_initialized)
				_config.domain = _domain;
		}
		
		public function get access():String { return _access; }
		public function set access(value:String):void 
		{
			if (_access == value)
				return;
			
			_access = value;
			
			if (_initialized)
				_config.access = _access;
		}
		
		public function get view():DisplayObject { return _view; }
		public function get ether():IEther { return _ether; }
		public function get config():EtherConfig { return _config.config; }
		public function get initialized():Boolean { return _initialized; }
		public function get applicationDomain():ApplicationDomain { return _config.applicationDomain; }
		
		public function get dependencies():IDependencies { return _dependencies; }
		public function get wiring():IWiring { return _wiring; }
		public function get mediators():IMediators { return _mediators; }
		public function get commands():ICommands { return _commands; }
		
		public function get destroyed():Boolean { return _destroyed; }
		public function set destroyed(value:Boolean):void { _destroyed = value; }
		
		public function get detached():Boolean { return _attached; }
		public function set detached(value:Boolean):void { _attached = value; }
		
		public function get destructionPolicy():String { return _destructionPolicy; }
		public function set destructionPolicy(value:String):void 
		{
			if (value != DestructionPolicy.AUTO && value != DestructionPolicy.DELAY && 
				value != DestructionPolicy.DETACH && value != DestructionPolicy.NEVER)
				throw new ArgumentError("Destruction policy '" + value + "' is not allowed.");
			
			_destructionPolicy = value; 
		}

		public function get destructionDelay():uint { return _destructionDelay; }
		public function set destructionDelay(value:uint):void { _destructionDelay = value; }
		
		public function get destructionCondition():Boolean { return _destructionCondition };
		public function set destructionCondition(value:Boolean):void { _destructionCondition = value};
		
		public function get started():Boolean { return _started; }
		public function set started(value:Boolean):void 
		{ 
			if (_started)
				throw new IllegalOperationError("IModule is already started.");
			
			_started = value;
			
			if (_started)
				completeParts();
		}
	}
}